#============ TRYNE_WEB - main module, that performs processing and web
from google.appengine.dist import use_library

use_library('django', '1.3')

import os
import operator
import sys
import logging
from google.appengine.ext.webapp import template
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

from tryne_db import *


#=============== This is global TODOs vault
#todo: Entity.delete()
#todo: Check all code for [deleted entities] vulnerability
#todo: set constants a fraction of daily load
#==========================================

#============ Interface constants
cQuestionTop = 20 # Number of top questions being displayed at once
cAnswersTop = 5  # Number of top answers being voted & displayed
cMaxFacts = 3  # Max num of facts being displayed at question summary
cMaxNodes = 10  # Max num of top nodes being displayed at node view - not tops yet


#============ Language parameters
Eng=1
Rus=2
defaultLanguage=Eng
lang=defaultLanguage # Interface language: 0 = English  1 = Russian



#============ Web pages generators

class WebRequest(webapp.RequestHandler):
    def GeneratePage(self, name,
                     values): # Generate page from template <name>. Adds header values (like corrent user name & so on)
        log('GeneratePage', 'template = ' + name + ' values = ' + str(values))
        values['user'] = users.get_current_user() # Current user and login/logout URL
        if values['user']: values['user_url'] = users.create_logout_url(self.request.uri)
        else: values['user_url'] = users.create_login_url(self.request.uri)
        values['logs'] = get_logs()
        values['rnd'] = random.randint(0,rndMax)
        #generate via template engine
        if lang==Rus:
            name='rus/'+name
        path = os.path.join(os.path.dirname(__file__), name + '.html')
        self.response.out.write(template.render(path, values))
        clear_logs()

    def UserGate(self): # stop anonymous access and go to Google login interface
        global lang;
        #if False: #not users.get_current_user():
        #    self.redirect(users.create_login_url(self.request.uri))
        #else:
        if self.request.get('lang'):
            lang=int(self.request.get('lang'))


#==================== Solution Tree ====================
class SolutionTree(WebRequest):
    def get(self):
        self.UserGate()
        quest = db.get(self.request.get('id'))
        view = self.request.get('view') # Solution view = [way,node,summary]
        if self.request.get('node'):
            node = db.get(self.request.get('node'))
        else: node = 0
        # if no node specified, get random end-node
        if not node and Node.all().filter('owner', quest).filter('childs', 0).count():
            while not node:
                node = Node.all().filter('owner', quest).filter('childs', 0).\
                filter('rnd >', random.randint(0, rndMax)).order('rnd').get()
            # Construct the way
        if view == 'way': # ======= Generate the solution way view
            if node:
                way = [node]
                while way[0].previous.class_name() == 'Node':
                    way[:0] = [way[0].previous]
            else: way = []
            self.GeneratePage('tree_way', {'quest': quest, 'node': node, 'way': way})
        else: # ========== Generate node view (default)
            childs = Node.all().filter('previous', node).order('-like').fetch(cMaxNodes)
            self.GeneratePage('tree_node', {'quest': quest, 'node': node, 'childs': childs})


class ListQuestions(WebRequest): # Start page and questions list for some set of tags, sorted via day popularity
    def get(self):
        self.UserGate()

        tagline = self.request.get('tags') # sting of comma-separated tags
        tags = tagline.split(',') # get tags tranferred via URL
        quests = Question.all()
        if tagline:
            for tag in tags: # filter for tags
                quests.filter('tags', tag)
        quests.order('-viewsToday').order('-views').fetch(cQuestionTop)
        if not quests.count(): quests = []
        # Todo: strip emails
        # Strip e-mails
        #for i in range(quests.count()):
        #    quests[i].nick=quests[i].author.nickname
        #    if quests[i].nick.count('@'):
        #        quests[i].nick = quests[i].nick [0:quests[i].nick.find('@')]
        # Generate tag cloud
        cloud = {}
        for quest in quests:
            for tag in quest.tags:
                if tag in cloud:
                    cloud[tag] += 1
                else: cloud[tag] = 1
        for tag in tags:
            if tag:
                del cloud[tag]
        scloud = sorted(cloud.iteritems(), key=operator.itemgetter(1), reverse=True)
        xcloud = []
        #for i in range(len(scloud)):
        #    t.tag=scloud[i][0]
        #    #t = [scloud[i][0],scloud[i][1]]
        #    xcloud.append(t)
        self.GeneratePage('list', {'tagline': tagline, 'tags': tags, 'quests': quests, 'cloud': scloud})


class ShowQuestion(WebRequest):
    def get(self): # display question summary and survey
        self.UserGate()
        try: quest = db.get(self.request.get('id'))
        except: self.redirect('/'); return
        if self.request.cookies.get('anoid',''):
            ano=int(self.request.cookies.get('anoid',''))
        else: ano=0 #TODO: Make anonymous cookies code nice
        if not ano:
            ano=random.randint(0,rndMax)
            self.response.headers.add_header(
                'Set-Cookie',
                'anoid='+str(ano)+'; expires=Fri, 31-Dec-2020 23:59:59 GMT')
        if users.get_current_user():
            imp = Impression.all().filter('user', users.get_current_user()).filter('question', quest).get()
        else: imp = Impression.all().filter('anoid', ano).filter('question', quest).get()
        if not imp:
            imp = Impression()
            if users.get_current_user():
                imp.user = users.get_current_user()
            else: imp.anoid = ano
            imp.question = quest
            imp.stage = 0
        stage = imp.stage
        log('ShowQuestion.get', 'Stage ' + str(stage))

        if stage == -1: # ====== Random poll select
            stage = cFinalStage
            r = random.randint(0, 100)
            if r < 10: stage -= 3 # do rate question
            elif r < 30: stage -= 2 # do Tag Revelance
            else: stage -= 1 # do Double seek

        if stage == 0: # ==== Show facts survey
            facts = GetRandomEntities('Fact', quest, cMaxFacts)
            if len(facts):
                self.GeneratePage('facts_survey', {'question': quest, 'facts': facts})
            else: stage += 1

        if stage == 1: # ==== Show important facts
            facts = Fact.all().filter('owner', quest).filter('like >', cImportantFact).filter('public', True).fetch(cMaxFacts)
            stage += 1
            if len(facts):
                facts.sort(key=liker)
                self.GeneratePage('important_facts', {'question': quest, 'facts': facts})
                imp.stage = stage
                imp.put()
                return # terninate funtction not to execute next steps now.

        if stage == 2: # ==== Show answers survey
            answers = GetRandomEntities('Answer', quest, cAnswersTop)
            self.GeneratePage('answer_survey', {'question': quest, 'answers': answers})

        if stage == 3: # ==== Show answers vote
            answers = RandomOrder(
                Answer.all().filter('owner', quest).filter('public', True).order('-like').fetch(cAnswersTop))
            if len(answers):
                self.GeneratePage('answer_vote', {'question': quest, 'answers': answers})
            else: stage = cFinalStage

        if stage == cFinalStage - 3: #==== Rate question poll
            log('Start Question poll')
            q = Question.all().filter('rnd >', random.randint(0, rndMax)).order('rnd').get()
            if q:
                self.GeneratePage('quest_rate', {'question': quest, 'q': q})
            else: stage = cFinalStage - 1

        if stage == cFinalStage - 2: #==== Tag relevance poll
            log('Start TagRelevance')
            q = Question.all().filter('rnd >', random.randint(0, rndMax)).order('rnd').get()
            if q and len(q.tags):
                self.GeneratePage('tag_rel',
                        {'question': quest, 'q': q, 'tag': q.tags[random.randint(0, len(q.tags)) - 1]})
            else: stage = cFinalStage - 1

        if stage == cFinalStage - 1: #==== Compare entities poll
            log('Start DecideCompare')
            if DecideCompare(quest):
                q = []
                if (Compare1().class_name() == 'Answer') or (
                Compare1().class_name() == 'Fact'): # hate Python that it makes me to write this awfull code
                    q = Compare1().owner
                self.GeneratePage('compare',
                        {'type': Compare1().class_name().lower(), 'question': quest, 'q': q, 'l': Compare1(),
                         'r': Compare2()})
            else: stage = cFinalStage

        if stage == cFinalStage: # ==== Display question summary
            answers = Answer.all().filter('owner', quest).filter('public', True).order('-tlike').fetch(cAnswersTop)

            # if no results, ignore public, should be after SOLVED
            if not answers: answers = Answer.all().filter('owner', quest).order('-tlike').fetch(cAnswersTop)

            # Check for SOLVED
            quest.answers = Answer.all().filter('owner', quest).count()
            quest.facts = Fact.all().filter('owner', quest).count()
            quest.nodes = Node.all().filter('owner', quest).count()
            quest.solved = (len(answers) > 1) and (answers[0].tlike > 20) and (
            answers[0].tlike > answers[1].like * 2) # SOLVED conditions
            quest.views += 1
            quest.put()
            if quest.solved: answers[0].solved = True

            if not quest.answers > len(answers): n_answers = 0
            facts = Fact.all().filter('owner', quest).filter('like >', cImportantFact).filter('public', True).order(
                '-like').fetch(cMaxFacts)
            # if no results, ignore Public
            if not facts: facts = Fact.all().filter('owner', quest).filter('like >', cImportantFact).order(
                '-like').fetch(cMaxFacts)
            if not quest.facts > len(facts): n_facts = 0
            self.GeneratePage('question', {'question': quest, 'answers': answers, 'facts': facts})
            stage = -1 # Set stage to poll select
        imp.stage = stage
        imp.put()

    def post(self): # process question surveys
        self.UserGate()
        try: quest = db.get(self.request.get('id'))
        except: self.redirect('/'); return
        if self.request.cookies.get('anoid',''):
            ano=int(self.request.cookies.get('anoid',''))
        else: ano=0
        if users.get_current_user():
            imp = Impression.all().filter('user', users.get_current_user()).filter('question', quest).get()
        else: imp = Impression.all().filter('anoid', ano).filter('question', quest).get()
        if not imp: raise Exception, 'Post without stage history'
        stage = imp.stage
        log('ShowQuestion.post', 'Stage ' + str(stage))
        if stage == 0: # ==== process facts survey
            facts = self.request.arguments() # This code is demi-copied from Answer Surwey
            for fact in facts: # todo: Unite this 2 processors into one
                if len(fact) < 12: continue # if argument name is shot, it is not database id
                res = self.request.get(fact)
                try:
                    fact = db.get(fact)
                    fact.imps += 1
                    if res == 'like': fact.likes += 1
                    if res == 'bad': fact.bads += 1
                    fact.update()
                except: pass
            stage += 1

        elif stage == 1: # ==== FAKE important facts
            raise Exception, 'Stage 1 post'

        elif stage == 2: # ==== Process answers survey
            answers = self.request.arguments()

            for answer in answers:
                if len(answer) < 12: continue # if argument name is shot, it is not database id
                res = self.request.get(answer)
                if not res: raise Exception, 'Bad result of Answer Survey'
                try:
                    answer = db.get(answer)
                    answer.imps += 1
                    if res == 'like': answer.likes += 1
                    if res == 'bad': answer.bads += 1
                    answer.update()
                except: pass
            stage += 1

        elif stage == 3: # ==== Process answers vote
            vote = self.request.get('vote')
            if not vote: raise Exception, 'Invalid vote!'
            i = 0
            while True:
                i += 1
                answerid = self.request.get(str(i))
                if not answerid: break
                try:
                    answer = db.get(answerid)
                    answer.timps += 1
                    if str(i) == vote: answer.tlikes += 1
                    answer.update()
                except: pass
            stage = cFinalStage

        elif stage == cFinalStage - 3: #==== Rate question poll
            try:
                q = db.get(self.request.get('q'))
                rate = self.request.get('rate')
                q.imps += 1
                if rate == 'like': q.likes += 1
                elif rate == 'bad': q.bads += 1
                q.update()
            except: pass
            stage = cFinalStage

        elif stage == cFinalStage - 2: #==== Tag relevance poll
            try:
                q = db.get(self.request.get('q'))
                tag = Tag.all().filter('text', self.request.get('tag')).get()
                ok = self.request.get('result')[0]==' '
                TagVote(tag, q, ok)
            except: pass
            stage = cFinalStage

        elif stage == cFinalStage - 1: #==== Compare entities poll
            compare = self.request.get('compare')
            win = self.request.get('lid')
            loose = self.request.get('rid')
            draw = False
            if compare == "Duplicate, delete first":
                win,loose = loose,win
            elif compare != "Duplicate, delete second":
                draw = True
            stage = cFinalStage
            try:
                win = db.get(win)
                loose = db.get(loose)
                DuplicateVote(win, loose, draw)
            except: pass
        else: stage = -1 # Random poll select

        imp.stage = stage
        imp.put()
        self.get()


class NewEntity(
    WebRequest): # display new entity window, trivial. Type=entity type. For Questions context tags are transferred.
    def get(self):
        self.UserGate()
        quest = self.request.get('id')
        if quest:
            try: quest = db.get(quest)
            except: self.redirect('/'); return
        type = self.request.get('type')
        tagline = self.request.get('tags') # get tags tranferred via URL       
        self.GeneratePage('new', {'question': quest, 'type': type, 'tagline': tagline})

class ImproveEntity(
    WebRequest): # display new entity window, trivial. Type=entity type. For Questions context tags are transferred.
    def get(self):
        self.UserGate()
        id = self.request.get('id')
        try: id = db.get(id)
        except: self.redirect('/'); return
        quest = self.request.get('quest')
        if quest:
            try: quest = db.get(quest)
            except: self.redirect('/'); return

        #type = self.request.get('type')
        #tagline = self.request.get('tags') # get tags tranferred via URL
        self.GeneratePage('improve', {'id': id, 'quest': quest})

    def post(self):
        self.UserGate()
        id = self.request.get('id')
        text = self.request.get('text')
        try: id = db.get(id)
        except: self.redirect('/'); return
        quest = self.request.get('quest')
        if quest:
            try: quest = db.get(quest)
            except: self.redirect('/'); return
        type=id.class_name()
        if text and not FindEntity(type, text):
            enty = 0
            if   type == 'Question': enty = Question().init(text)
            elif type == 'Answer':   enty = Answer().init(text, quest)
            elif type == 'Fact':     enty = Fact().init(text, quest)
            elif type == 'Node':     enty = Node().init(text, quest, id.previous)
            elif type == 'tag':
                text = PrepareTagName(text)
                if not PrepareTagName(text) or text[1] == '*':
                    self.redirect('/')
                    return
                enty = Tag().init(text)
            if enty:
                enty.put()
                DuplicateVote(enty,id)
        if quest:
            self.redirect('/quest?id='+str(quest.key()))
        else:self.redirect('/')



class AddEntity(WebRequest): # Process entity addition
    def post(self):
        self.UserGate()
        text = self.request.get('text')
        type = self.request.get('type')
        tags = []
        if not text: # Check text for emptyness
            self.GeneratePage('addreport', {'error': 'Text is empty', 'type': type, 'text': text})
            return
        quest = self.request.get('id')
        if type in ['answer', 'fact', 'tags', 'node']: quest = db.get(quest)

        if   type == 'question': enty = Question().init(text)
        elif type == 'answer':   enty = Answer().init(text, quest)
        elif type == 'fact':     enty = Fact().init(text, quest)
        elif type == 'node':
            previous = db.get(self.request.get('previous'))
            enty = Node().init(text, quest, previous)
        elif type == 'tag':
            text = PrepareTagName(text)
            if not PrepareTagName(text) or text[1] == '*':
                self.GeneratePage('addreport', {'error': 'Invalid tag name', 'type': type, 'text': text})
                return
            enty = Tag().init(text)
        elif type == 'tags':
            tags = text.split(',')
            for i in range(len(tags)):
                tags[i] = PrepareTagName(tags[i])
                if not tags[i]: continue
                if tags[i][0] == '*':
                    self.GeneratePage('addreport',
                            {'error': 'Invalid tag name: ' + tags[i][1:len(tags[i])], 'type': 'tag', 'text': quest.text,
                             'tags': tags})
                    return
                tagObject = FindEntity('tag', tags[i])
                if not tagObject: tagObject = Tag().init(tags[i])
                tagObject.put()
                tagObject.link(quest)
            quest.put()
            self.GeneratePage('addreport', {'question': quest, 'type': 'tags', 'text': text, 'tags': quest.tags,
                                            'next': '/quest?id=' + str(quest.key())})
            return
        else: raise Exception, 'Invalid entity type in AddEntity'
        # check for identical entries
        if FindEntity(type, text):
            self.GeneratePage('addreport',
                    {'error': 'Same text ' + type + ' already exists', 'type': type, 'text': text})
            return
            # ----- Question and tag code, not too nice :-)
        if type == 'question':
            tags = self.request.get('tags').split(',')
            for i in range(len(tags)):
                tags[i] = PrepareTagName(tags[i])
                if not tags[i]: continue
                if tags[i][0] == '*':
                    self.GeneratePage('addreport',
                            {'error': 'Invalid tag name: ' + tags[i][1:len(tags[i])], 'type': type, 'text': text,
                             'tags': tags})
                    return
                tagObject = FindEntity('tag', tags[i])
                if not tagObject: tagObject = Tag().init(tags[i])
                tagObject.link(enty)
                tagObject.put()
        enty.put()
        if type == 'question':
            next = '/quest?id=' + str(enty.key())
        elif type == 'node':
            type = 'solution node'
            next = '/tree?id=' + str(quest.key()) + '&node=' + str(enty.key())
        else: next = '/quest?id=' + str(quest.key())
        self.GeneratePage('addreport', {'question': quest, 'type': type, 'text': text, 'tags': tags, 'next': next})


def liker(enty): # helper function for sorting
    return enty.like

application = webapp.WSGIApplication(
    [('/', ListQuestions),
        ('/list', ListQuestions), # List top questions at certain tags
        ('/quest', ShowQuestion), # display question (and its survey)
        ('/new', NewEntity), # New entity dialog
        ('/add', AddEntity), # Process entity addition
        ('/improve', ImproveEntity), # Process entity addition
        ('/tree', SolutionTree)    # Process entity addition
    ],
     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()